/*
 * Copyright (c) 2024 Soon.Liao. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <unistd.h>
#include <securec.h>

#include "i2c.h"
#include "gpio.h"
#include "main.h"
#include "debug.h"
#include "i2c_common.h"
#include "ssd1306.h"
#include "paj7620u2.h"
#include "paj7620u2_entry.h"

#define PAJ_7620_U2_ENTRY_TIME 200 // 等待I2C初始化完成时间ms
#define PAJ_7620_U2_QUIT_TIME 300  // 每次查询时间间隔单位ms
#define PAJ_7620_U2_DEFAULT_RW_LEN 1
#define PAJ_7620_U2_DEFAULT_RWU16_LEN 2
// I2C settings
#define PAJ_7620_U2_MASTER_WRITE_ADDR ((PAJ_7620_U2_I2C_ADDRESS << 1) + 0)
#define PAJ_7620_U2_MASTER_READ_ADDR ((PAJ_7620_U2_I2C_ADDRESS << 1) + 1)
#define PAJ_7620_U2_BYTE_LEN 8

static unsigned int paj7620u2_byte_combine(unsigned char b1, unsigned char b2)
{
    return (unsigned int)((b2) << PAJ_7620_U2_BYTE_LEN) + (b1);
}

/**
 * @brief Write the address of paj7620u2.
 * @param addrBuffer The address of paj7620u2.
 * @param length The length of data buffer.
 * @retval BASE status type: OK, ERROR, BUSY, TIMEOUT.
 */
static BASE_StatusType paj7620u2_write(unsigned char *addrBuffer, unsigned int length)
{
    BASE_StatusType ret = BASE_STATUS_OK;
    BASE_FUNC_DELAY_MS(IIC_READ_WRITE_DELAY_MS); /* Delay 5 ms. */
    ret = HAL_I2C_MasterWriteDMA(&g_i2c0_master, PAJ_7620_U2_MASTER_WRITE_ADDR, addrBuffer, length);
    if (ret != BASE_STATUS_OK) {
        DBG_PRINTF("LINE:%d,Write Data Fail,ret:%d\r\n", __LINE__, ret);
        return ret;
    }
    /* Waiting for write completion or failure */
    while (!(get_tx_done_flag() || get_error_flag())) {
        ;
    }
    set_tx_done_flag(BASE_CFG_UNSET);
    if (get_error_flag() == BASE_CFG_SET) {
        set_error_flag(BASE_CFG_UNSET); /* Reset g_error_flag. */
        DBG_PRINTF("LINE:%d,Write Data Fail\r\n", __LINE__);
        return BASE_STATUS_ERROR;
    }
    return ret;
}

static BASE_StatusType paj7620u2_read(unsigned char *buffer, unsigned int buffLen)
{
    BASE_FUNC_DELAY_MS(IIC_READ_WRITE_DELAY_MS); /* Delay 5 ms. */
    BASE_StatusType retval = HAL_I2C_MasterReadDMA(&g_i2c0_master, PAJ_7620_U2_MASTER_READ_ADDR, buffer, buffLen);
    if (retval != BASE_STATUS_OK) {
        DBG_PRINTF("paj7620u2_read(0x%X) failed, %0X!\n", buffer[1], retval);
        return retval;
    }
    /* Waiting for read completion or failure */
    while (!(get_rx_done_flag() || get_error_flag())) {
        ;
    }
    set_rx_done_flag(BASE_CFG_UNSET);
    if (get_error_flag() == BASE_CFG_SET) {
        DBG_PRINTF("LINE:%d,Read Data Fail\r\n", __LINE__);
        set_error_flag(BASE_CFG_UNSET);
        return BASE_STATUS_ERROR;
    }
    return BASE_STATUS_OK;
}

/******************* PAJ7620U2 Driver Interface *****************************/
static unsigned char i2c_read_byte(int reg)
{
    BASE_StatusType retval = 0;
    unsigned char addr_buff[1] = {reg};
    unsigned char buffer[1] = {0x00};
    retval = paj7620u2_write(addr_buff, PAJ_7620_U2_DEFAULT_RW_LEN);
    if (retval != 0) {
        DBG_PRINTF("PAJ7620U2 i2c_read_byte fail line:%d", __LINE__);
        return retval;
    }
    retval = paj7620u2_read(buffer, PAJ_7620_U2_DEFAULT_RW_LEN);
    if (retval != 0) {
        DBG_PRINTF("PAJ7620U2 i2c_read_byte fail line:%d", __LINE__);
        return retval;
    }
    return buffer[0];
}

static unsigned int i2c_read_u16(int reg)
{
    int ret;
    unsigned int data = 0x00;
    unsigned char addr_buf[1] = {reg};
    unsigned char read_buf[2];
    ret = paj7620u2_write(addr_buf, PAJ_7620_U2_DEFAULT_RW_LEN);
    if (ret != 0) {
        DBG_PRINTF("PAJ7620U2 i2c_read_u16 fail line:%d", __LINE__);
        return ret;
    }
    ret = paj7620u2_read(read_buf, PAJ_7620_U2_DEFAULT_RWU16_LEN);
    if (ret != BASE_STATUS_OK) {
        DBG_PRINTF("PAJ7620U2 i2c_read_u16 0x%X,0x%X ret=%d\r\n", read_buf[0], read_buf[1], ret);
        return ret;
    }
    data = paj7620u2_byte_combine(read_buf[0], read_buf[1]);
    return data;
}

static void i2c_write_byte(int reg, int val)
{
    BASE_StatusType retval = 0;
    unsigned char data_send[2] = {reg, val};
    retval = paj7620u2_write(data_send, PAJ_7620_U2_DEFAULT_RWU16_LEN);
    if (retval != BASE_STATUS_OK) {
        DBG_PRINTF("I2C_writeByte(reg=0x%X,val=0x%X) failed, retval=%d!\r\n", reg, val, retval);
        return;
    }
    return;
}

static void paj7620u2_show_data(char *data, unsigned int len)
{
    BASE_FUNC_UNUSED(len);
    int x = 0;
    int y = 20;
    ssd1306_fill(BLACK);
    ssd1306_set_cursor(x, y);
    ssd1306_draw_string(data, g_font_7x10, WHITE);
    ssd1306_update_screen();
}

static void on_paj7620u2_event_up(void)
{
    unsigned int gesture_data;
    BASE_FUNC_DELAY_MS(PAJ_7620_U2_QUIT_TIME);
    gesture_data = i2c_read_u16(PAJ_INT_FLAG1);
    DBG_PRINTF("Double Confirm Gesture Sensor gesture_data=0x%x\r\n ", gesture_data);
    if (gesture_data == PAJ_CLOCKWISE) {
        DBG_PRINTF("Clockwise\r\n");
        paj7620u2_show_data(STR_CLOCKWISE, STR_CLOCKWISE_LEN);
    } else {
        DBG_PRINTF("Up\r\n");
        paj7620u2_show_data(STR_UP, STR_UP_LEN);
    }
}

static void on_paj7620u2_event_down(void)
{
    unsigned int gesture_data;
    BASE_FUNC_DELAY_MS(PAJ_7620_U2_QUIT_TIME);
    gesture_data = i2c_read_u16(PAJ_INT_FLAG1);
    DBG_PRINTF("Double Confirm Gesture Sensor gesture_data=0x%x\r\n ", gesture_data);
    if (gesture_data == PAJ_CLOCKWISE) {
        DBG_PRINTF("AntiClockwise\r\n");
        paj7620u2_show_data(STR_COUNT_CLOCKWISE, STR_COUNT_CLOCKWISE_LEN);
    } else {
        DBG_PRINTF("Down\r\n");
        paj7620u2_show_data(STR_DOWN, STR_DOWN_LEN);
    }
}

static void on_paj7620u2_event_left(void)
{
    unsigned int gesture_data;
    BASE_FUNC_DELAY_MS(PAJ_7620_U2_QUIT_TIME);
    gesture_data = i2c_read_u16(PAJ_INT_FLAG1);
    DBG_PRINTF("Double Confirm Gesture Sensor gesture_data=0x%x\r\n ", gesture_data);
    if (gesture_data == PAJ_CLOCKWISE) {
        DBG_PRINTF("AntiClockwise\r\n");
        paj7620u2_show_data(STR_COUNT_CLOCKWISE, STR_COUNT_CLOCKWISE_LEN);
    } else {
        DBG_PRINTF("Left\r\n");
        paj7620u2_show_data(STR_LEFT, STR_LEFT_LEN);
    }
}

static void on_paj7620u2_event_right(void)
{
    unsigned int gesture_data;
    BASE_FUNC_DELAY_MS(PAJ_7620_U2_QUIT_TIME);
    gesture_data = i2c_read_u16(PAJ_INT_FLAG1);
    DBG_PRINTF("Double Confirm Gesture Sensor gesture_data=0x%x\r\n ", gesture_data);
    if (gesture_data == PAJ_CLOCKWISE) {
        DBG_PRINTF("Clockwise\r\n");
        paj7620u2_show_data(STR_CLOCKWISE, STR_CLOCKWISE_LEN);
    } else {
        DBG_PRINTF("Right\r\n");
        paj7620u2_show_data(STR_RIGHT, STR_RIGHT_LEN);
    }
}

static void on_paj7620u2_event_forward(void)
{
    DBG_PRINTF("Forward\r\n");
    paj7620u2_show_data(STR_FORWARD, STR_FORWARD_LEN);
}

static void on_paj7620u2_event_backward(void)
{
    DBG_PRINTF("Backward\r\n");
    paj7620u2_show_data(STR_BACKWARD, STR_BACKWARD_LEN);
}

static void on_paj7620u2_event_clockwise(void)
{
    DBG_PRINTF("Clockwise\r\n");
    paj7620u2_show_data(STR_CLOCKWISE, STR_CLOCKWISE_LEN);
}

static void on_paj7620u2_event_anticlockwise(void)
{
    DBG_PRINTF("AntiClockwise\r\n");
    paj7620u2_show_data(STR_COUNT_CLOCKWISE, STR_COUNT_CLOCKWISE_LEN);
}

static void on_paj7620u2_event_wave(void)
{
    DBG_PRINTF("Wave\r\n");
    paj7620u2_show_data(STR_WAVE, STR_WAVE_LEN);
}

void paj7620u2_event(void)
{
    unsigned int gesture_data;
    BASE_FUNC_DELAY_MS(PAJ_7620_U2_QUIT_TIME);
    gesture_data = i2c_read_u16(PAJ_INT_FLAG1);
    if (gesture_data) {
        DBG_PRINTF("Gesture Sensor gesture_data=0x%x\r\n ", gesture_data);
        switch (gesture_data) {
            case PAJ_UP:
                on_paj7620u2_event_up();
                break;
            case PAJ_DOWN:
                on_paj7620u2_event_down();
                break;
            case PAJ_LEFT:
                on_paj7620u2_event_left();
                break;
            case PAJ_RIGHT:
                on_paj7620u2_event_right();
                break;
            case PAJ_FORWARD:
                on_paj7620u2_event_forward();
                break;
            case PAJ_BACKWARD:
                on_paj7620u2_event_backward();
                break;
            case PAJ_CLOCKWISE:
                on_paj7620u2_event_clockwise();
                break;
            case PAJ_COUNT_CLOCKWISE:
                on_paj7620u2_event_anticlockwise();
                break;
            case PAJ_WAVE:
                on_paj7620u2_event_wave();
                break;
            default:
                break;
        }
        gesture_data = 0;
    }
}

static unsigned int paj7620u2_init(void)
{
    unsigned char i, state;

    state = i2c_read_byte(0x00); // Read state
    DBG_PRINTF("Gesture Sensor state =0x%x\r\n", state);
    if (state != PAJ_INIT_OK) {
        return BASE_STATUS_ERROR;
    } // Wake up failed
    i2c_write_byte(PAJ_BANK_SELECT, 0); // Select Bank 0
    for (i = 0; i < INIT_ARRAY; i++) {
        i2c_write_byte(g_init_register_array[i][0], g_init_register_array[i][1]); // Power up initialize
    }
    i2c_write_byte(PAJ_BANK_SELECT, 0);
    return BASE_STATUS_OK;
}

BASE_StatusType paj7620u2_entry(void)
{
    int ret = -1;
    unsigned int i;
    DBG_PRINTF("Gesture Sensor Program Paj7620U2Task init start...\r\n");
    BASE_FUNC_DELAY_MS(PAJ_7620_U2_ENTRY_TIME);
    ret = paj7620u2_init();
    if (ret != BASE_STATUS_OK) {
        DBG_PRINTF("Gesture Sensor Error\r\n");
        return BASE_STATUS_ERROR;
    }
    DBG_PRINTF("Gesture Sensor Entry2\r\n");
    i2c_write_byte(PAJ_BANK_SELECT, 0); // Select Bank 0
    for (i = 0; i < GESTURE_ARRAY_SIZE; i++) {
        i2c_write_byte(GESTURE_INIT_ARRAY[i][0], GESTURE_INIT_ARRAY[i][1]); // Gesture register initializes
    }
    DBG_PRINTF("Gesture Sensor OK\r\n");
    return BASE_STATUS_OK;
}
